package com.planet_ink.ZipCodeWeather.services;

import com.planet_ink.ZipCodeWeather.model.CurrentWeatherCondition;
import com.planet_ink.ZipCodeWeather.model.ForecastWeatherCondition;
import com.planet_ink.ZipCodeWeather.model.WeatherForecasts;
import com.planet_ink.ZipCodeWeather.model.WeatherForecast;
import com.planet_ink.ZipCodeWeather.services.MiniJSON.JSONObject;
import com.planet_ink.ZipCodeWeather.services.MiniJSON.MJSONException;

/**
 * Static singleton class that converts from Yahoo YQL response JSON doc to mapped
 * WeatherCondition objects in the model, for return to the web resource  
 * @author Bo Zimmerman
 */
public class WeatherTransformer
{
	private static Object[] getMultipleWeatherChannels(JSONObject json) throws MJSONException
	{
		final JSONObject query = json.getCheckedJSONObject("query");
		final JSONObject results = query.getCheckedJSONObject("results");
		return results.getCheckedArray("channel");
	}
	
	private static JSONObject getWeatherChannel(JSONObject json) throws MJSONException
	{
		final JSONObject query = json.getCheckedJSONObject("query");
		final JSONObject results = query.getCheckedJSONObject("results");
		return results.getCheckedJSONObject("channel");
	}
	
	private static CurrentWeatherCondition getCurrentConditionFromItem(JSONObject json) throws MJSONException
	{
		final String temperatureStr = json.getCheckedString("temp");
		final String dateTime = json.getCheckedString("date");
		final String text = json.getCheckedString("text");
		try
		{
			final int temp = Integer.parseInt(temperatureStr);
			return new CurrentWeatherCondition(temp, text, dateTime);
		}
		catch(NumberFormatException e)
		{
			throw new MJSONException("Invalid temperature in JSON response detected");
		}
	}
	
	private static ForecastWeatherCondition setForecastConditionFromJSON(JSONObject json, ForecastWeatherCondition condition) throws MJSONException
	{
		final String lowTemperatureStr = json.getCheckedString("low");
		final String highTemperatureStr = json.getCheckedString("high");
		final String dateTime = json.getCheckedString("date");
		final String text = json.getCheckedString("text");
		try
		{
			final int low = Integer.parseInt(lowTemperatureStr);
			final int high = Integer.parseInt(highTemperatureStr);
			condition.setHigh(Integer.valueOf(high));
			condition.setLow(Integer.valueOf(low));
			condition.setText(text);
			condition.setDateTimeDescription(dateTime);
			return condition;
		}
		catch(NumberFormatException e)
		{
			throw new MJSONException("Invalid temperature in JSON response detected");
		}
	}
	
	private static WeatherForecast getWeatherForecastfromChannelJSON(JSONObject channel) throws MJSONException
	{
		final JSONObject channelItem = channel.getCheckedJSONObject("item");
		final WeatherForecast forecast = new WeatherForecast();
		final JSONObject currentConditions = channelItem.getCheckedJSONObject("condition");
		forecast.setCurrent(getCurrentConditionFromItem(currentConditions));
		final Object[] forecastConditions = channelItem.getCheckedArray("forecast");
		if(forecastConditions.length > 0)
		{
			if (forecastConditions[0] instanceof JSONObject)
			{
				forecast.setToday(setForecastConditionFromJSON((JSONObject)forecastConditions[0], new ForecastWeatherCondition()));
			}
			else
				throw new MJSONException("Invalid today forecast in JSON response detected");
			if (forecastConditions[1] instanceof JSONObject)
			{
				forecast.setTomorrow(setForecastConditionFromJSON((JSONObject)forecastConditions[1], new ForecastWeatherCondition()));
			}
			else
				throw new MJSONException("Invalid tomorrow forecast in JSON response detected");
		}
		else
			throw new MJSONException("No forecast in JSON response detected");
		return forecast;
	}
	
	public static WeatherForecast fromSingleYahooJSON(JSONObject json) throws MJSONException
	{
		final JSONObject firstChannel = getWeatherChannel(json);
		return getWeatherForecastfromChannelJSON(firstChannel);
	}
	
	public static WeatherForecasts fromMultipleYahooJSON(String[] zipCodes, JSONObject json) throws MJSONException
	{
		final WeatherForecasts forecasts = new WeatherForecasts();
		if(zipCodes.length==1)
		{
			forecasts.getWeather().put(zipCodes[0], fromSingleYahooJSON(json));
		}
		else
		{
			final Object[] channels = getMultipleWeatherChannels(json);
			if(channels.length != zipCodes.length)
			{
				throw new MJSONException("Insufficient channels in JSON response detected");
			}
			for(int i=0;i<channels.length; i++)
			{
				final String zipCode = zipCodes[i];
				final Object channelObj = channels[i];
				if(channelObj instanceof JSONObject)
				{
					if(!forecasts.getWeather().containsKey(zipCode))
					{
						forecasts.getWeather().put(zipCode, getWeatherForecastfromChannelJSON((JSONObject)channelObj));
					}
				}
				else
					throw new MJSONException("Invalid channel #"+(i+1)+" in JSON response detected");
			}
		}
		return forecasts;
	}
	
}
